package commons.ihm;

import commons.params_appli.ParamsAppli;
import commons.drawing.UtilsDrawing.DrawQuality;
import commons.utils.primitive_geom.Point_d;
import commons.utils.primitive_geom.Rect_d;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Stroke;

public class ParametresAffichageCarto {

	public enum TypeAffichage {PAPIER, TOPO};
	public enum ModeAffichage {NORMAL, SELECTION, HIGHLIGHT};
	
	// caractéristique de l'écran d'ordinateur utilisé !
	private double TAILLE_PIXEL_METRE;
	
	private double echelleVisualisation;
	public double getEchelleVisualisation() {return echelleVisualisation;}

	public Color colorSelection;
	public Color colorHighlight;

	private int LARGEUR_PANEL_px;
	private int HAUTEUR_PANEL_px;
	
	private Point_d ptView_m;
	public Point_d getPtView_m() {return ptView_m;}
	
	private double largView_m;
	private double hautView_m;
	private Rect_d rectView_m;

	public Rect_d getRectView() {return rectView_m;}

	public TypeAffichage typeAffichage;
	public ModeAffichage modeAffichage;

	// Paramètres qualité
	// qualité de rendu
	public DrawQuality drawQuality;
	// simplification tracé linéaires
	public double toleranceApproxLine_px;
	
	public ParametresAffichageCarto() {
	    TAILLE_PIXEL_METRE = ParamsAppli.getParam_d("Autogene.TAILLE_PIXEL");
		toleranceApproxLine_px = ParamsAppli.getParam_d("Autogene.TOLERANCE_SIMPLIFICATION_LINE_PX");

		ptView_m = new Point_d(0,0);

		modeAffichage = ModeAffichage.NORMAL;

		drawQuality = DrawQuality.PRECISE;
		initStrokes();

		colorSelection = ParamsAppli.getParam_c("AutoGene.COULEUR_SELECTION");
		colorHighlight = ParamsAppli.getParam_c("AutoGene.COULEUR_SURBRILLANCE");
	}

	// optimisation d'affichage
	double kxX=0;
	double kyY=0;
	double klgLG = 0;

	private void setParamDisplay (int largPanel_px, int hautPanel_px, Point_d ptView_m, double echelleVisualisation) {
		
		// Initialisation des variables
		this.ptView_m = ptView_m.getCopy();
		this.LARGEUR_PANEL_px = largPanel_px;
		this.HAUTEUR_PANEL_px = hautPanel_px;
	    this.echelleVisualisation = echelleVisualisation;
		
		// Calcul du rectangle de vue (m)
		largView_m = TAILLE_PIXEL_METRE * LARGEUR_PANEL_px / echelleVisualisation;
		hautView_m = largView_m * ((double)HAUTEUR_PANEL_px / (double)LARGEUR_PANEL_px);
		rectView_m = new Rect_d (ptView_m, largView_m, hautView_m);

		kxX = LARGEUR_PANEL_px / largView_m;
		kyY = HAUTEUR_PANEL_px / hautView_m;
		klgLG = echelleVisualisation / TAILLE_PIXEL_METRE;
	}
	
	
	// Initialise l'affichage pour englober un rectangle d'emprise
	public void extendsViewToRect (Rect_d rectEmprise_m, int LARG_PANEL_px, int HAUT_PANEL_px) {
	    setParamDisplay (LARG_PANEL_px, HAUT_PANEL_px, // taille du panel d'affichage de la carte
						 rectEmprise_m.getCenter(), // centre de vue
						 Math.min (LARG_PANEL_px*TAILLE_PIXEL_METRE / rectEmprise_m.getWidth(), // on prend la plus petite echelle possible pour etre sur de voir au moins tout rectEmprise
								   HAUT_PANEL_px*TAILLE_PIXEL_METRE / rectEmprise_m.getHeight())
						 );
	}

	// zoom
	public void changeEchelle (double newEchelle) {
	    setParamDisplay (LARGEUR_PANEL_px, HAUTEUR_PANEL_px, ptView_m, newEchelle);
	}

	public void changeEchelle (double newEchelle, Point_d ptInvariant) {
		// On change l'échelle, mais on fait un petit scroll en meme temps pour laisser invariant, en terme de
		//  position d'affichage, le pt donné en argument

		// calcul d'un nouveau pt de vue central
		double k = (1/echelleVisualisation - 1/newEchelle) / (1/echelleVisualisation);
		Point_d newPtView_m = new Point_d(ptView_m.x + k*(ptInvariant.x-ptView_m.x), ptView_m.y + k*(ptInvariant.y-ptView_m.y));

	    setParamDisplay (LARGEUR_PANEL_px, HAUTEUR_PANEL_px, newPtView_m, newEchelle);
	}


	//simple scroll
	public void changePtView (Point_d newPtView_m) {
		setParamDisplay (LARGEUR_PANEL_px, HAUTEUR_PANEL_px, newPtView_m, echelleVisualisation);
	}
	public void changeTaillePanel (int newLargeurPanel, int newHauteurPanel) {
		setParamDisplay (newLargeurPanel, newHauteurPanel, ptView_m, echelleVisualisation);
	}	
	
	// on renvoit un float car c'est ce qui sert pour les fonction graphiques, et ca evite des conversion de type ultérieurement
	public float x2X (double x_m) {
		//return (float) ((x_m - rectView_m.xMin) / largView_m * LARGEUR_PANEL_px);
		return (float) ((x_m - rectView_m.xMin) * kxX);
	}
	// on renvoit un float car c'est ce qui sert pour les fonction graphiques, et ca evite des conversion de type ultérieurement
	public float y2Y (double y_m) {
		//return (float) ((rectView_m.yMax - y_m) / hautView_m * HAUTEUR_PANEL_px);
		return (float) ((rectView_m.yMax - y_m) * kyY);
	}
	
	public double X2x (double X_px) {
		return  largView_m * X_px / LARGEUR_PANEL_px + rectView_m.xMin;
	}
	public double Y2y (double Y_px) {
		return  - hautView_m * Y_px / HAUTEUR_PANEL_px + rectView_m.yMax;
	}
	
	public float lg2LG (double lg) {
		//return (float) (lg * echelleVisualisation / NB_METRE_PAR_PIXEL);
		return (float) (lg * klgLG);
	}
	public double LG2lg (double LG) {
		return (LG * TAILLE_PIXEL_METRE / echelleVisualisation);
	}

	
	private Stroke basicStroke1;
	private Stroke basicStroke2;
	private Stroke dashStroke1;
	private Stroke dashStroke2;
	private Stroke currentStroke; // utilisé uniquement pour les stroke NON DASH
	private float currentWidth=-10;

	public Stroke getBasicStroke1() {return basicStroke1;}
	public Stroke getBasicStroke2() {return basicStroke2;}
	public Stroke getDashStroke1() {return dashStroke1;}

	public Stroke getDashStroke(float width) {
		if (width == 1)
			return dashStroke1;
		if (width == 2)
			return dashStroke2;
		
		float[] dashes = {5.0f, 5.0f};
		return new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, -1, dashes, 0f);
	}
	
	public Stroke getBasicStroke(float width) {
		// TODO_OK PERF : a optimiser !!
		if (width == 1)
			return basicStroke1;
		if (width == 2)
			return basicStroke2;

		if (width==currentWidth)
			return currentStroke;

		currentStroke = new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
		currentWidth = width;
		return currentStroke;
	}

	private void initStrokes() {
		basicStroke1 = new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
		basicStroke2 = new BasicStroke(2f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);

		float[] dashes = {5.0f, 5.0f};
		dashStroke1 = new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, -1, dashes, 0f);
		dashStroke2 = new BasicStroke(2f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, -1, dashes, 0f);
	}


	public Color getColorSelonModeAffichage(Color normalColor) {
		if (modeAffichage == ModeAffichage.NORMAL)
			return normalColor;
		else if (modeAffichage == ModeAffichage.SELECTION)
			return colorSelection;
		else if (modeAffichage == ModeAffichage.HIGHLIGHT)
			return colorHighlight;
		else
			assert(false);
		return null;
	}

}